//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_DATETIME_CTIMEOFDAY_H_
#define _BUOLA_DATETIME_CTIMEOFDAY_H_

#include <buola/buola.h>
#include <buola/datetime/chrono.h>
#include <buola/utility/operators.h>

namespace buola {

///\addtogroup datetime
///@{

////////////////////////////////////////////////////////////////////////////

/// time of day

/// Represents the time of day
///
/// It offers explicit conversion to and from a time duration, representing
/// the time elapsed since the beginning of the day
///
///////////////////////////////////////////////////////////////////////////

class CTimeOfDay : public ops::UComparison<CTimeOfDay>
{
    static const int64_t cHour=3600000000000L;
    static const int64_t cMin=60000000000L;
    static const int64_t cSec=1000000000L;

public:
    ///default constructor

    ///Initializes the time duration to 0
    CTimeOfDay()
        :   mDuration(0)
    {}

    ///copy constructor
    CTimeOfDay(const CTimeOfDay &pDuration)
        :   mDuration(pDuration.mDuration)
    {}

    ///constructs from hours, minutes, seconds and nanoseconds
    CTimeOfDay(int pHour,int pMin,int pSec=0,int64_t pNanoSec=0)
        :   mDuration(pHour*cHour+pMin*cMin+pSec*cSec+pNanoSec)
    {}

    ///construct from a point in calendar time

    ///\param pTime the point of time
    ///\param pTZ the timezone to use for the conversion. Defaults to local time.
    explicit CTimeOfDay(const CCalendarTime &pTime,const CTimeZone& pTZ=CTimeZone());

    ///conversion from time duration
    template<typename tRep,typename tPeriod>
    explicit CTimeOfDay(const chrono::duration<tRep,tPeriod> &pDuration)
        :   mDuration(chrono::duration_cast<chrono::nanoseconds>(pDuration))
    {}

    ///sets from hours, minutes, seconds and nanoseconds
    void Set(int pHour,int pMin,int pSec=0,int64_t pNanoSec=0)
    {
        mDuration=chrono::nanoseconds(pHour*cHour+pMin*cMin+pSec*cSec+pNanoSec);
    }

    ///returns true if the object represents a well-formed date
    bool IsWellFormed()
    {
        return (mDuration.count()>=0&&mDuration<chrono::hours(24));
    }

    ///returns the time as text, according to current locale
    std::wstring GetText() const;
    ///returns the time as text, in hh:mm 24-hour format
    std::wstring GetHourMinText() const;

    ///returns the hour part of the time (represented as hh:mm:ss.ns)
    int Hour() const
    {
        return mDuration.count()/cHour;
    }

    ///returns the minute part of the time (represented as hh:mm:ss.ns)

    ///it will return a value in the range [0,59]
    int Min() const
    {
        return (mDuration.count()/cMin)%60;
    }

    ///returns the second part of the time (represented as hh:mm:ss.ns)

    ///it will return a value in the range [0,59]
    int Sec() const
    {
        return (mDuration.count()/cSec)%60;
    }

    ///returns the nanosecond part of the time (represented as hh:mm:ss.ns)
    int64_t Nanosec() const
    {
        return mDuration.count()%cSec;
    }

    ///returns the total number of seconds (including completed minutes and hours)
    int64_t TotalSeconds() const
    {
        return mDuration.count()/1000000000L;
    }

    ///returns the total number of nanoseconds (including completed seconds)
    int64_t TotalNanoseconds() const
    {
        return mDuration.count();
    }

    ///sets the hour part of the time (represented as hh:mm:ss.ns)
    void SetHour(int pHour)
    {
        mDuration=chrono::nanoseconds(mDuration.count()%cHour+pHour*cHour);
    }

    ///sets the minute part of the time (represented as hh:mm:ss.ns)
    void SetMin(int pMin)
    {
        mDuration=chrono::nanoseconds(mDuration.count()+(pMin-Min())*cMin);
    }

    ///sets the second part of the time (represented as hh:mm:ss.ns)
    void SetSec(int pSec)
    {
        mDuration=chrono::nanoseconds(mDuration.count()+(pSec-Sec())*cSec);
    }

    ///sets the nanosecond part of the time (represented as hh:mm:ss.ns)
    void SetNanosec(int64_t pNanosec)
    {
        mDuration=chrono::nanoseconds(mDuration.count()+pNanosec-Nanosec());
    }

    ///returns the total number of seconds as a floating point value
    double ToDouble() const
    {
        return (double)mDuration.count()/1000000000.0;
    }

    ///equality operator
    bool operator==(const CTimeOfDay &pRH) const
    {
        return mDuration==pRH.mDuration;
    }

    ///comparison less-than operator
    bool operator<(const CTimeOfDay &pRH) const
    {
        return mDuration<pRH.mDuration;
    }

    ///adds another time duration to this object
    CTimeOfDay &operator+=(const CTimeOfDay &pRH)
    {
        mDuration+=pRH.mDuration;
        return *this;
    }

    ///subtracts another time duration from this object
    CTimeOfDay &operator-=(const CTimeOfDay &pRH)
    {
        mDuration-=pRH.mDuration;
        return *this;
    }

    ///multiplies the value of this object by the given integer
    CTimeOfDay &operator*=(int64_t pRH)
    {
        mDuration*=pRH;
        return *this;
    }

    ///divides the value of this object by the given integer
    CTimeOfDay &operator/=(int64_t pRH)
    {
        mDuration/=pRH;
        return *this;
    }

    ///multiplies the value of this object by the given real number
    CTimeOfDay &operator*=(double pRH)
    {
        mDuration=chrono::nanoseconds((int64_t)((double)mDuration.count()*pRH));
        return *this;
    }

    ///divides the value of this object by the given real number
    CTimeOfDay &operator/=(double pRH)
    {
        mDuration=chrono::nanoseconds((int64_t)((double)mDuration.count()/pRH));
        return *this;
    }

    ///returns the addition of \p pRH to this object
    CTimeOfDay operator+(const CTimeOfDay &pRH) const
    {
        return CTimeOfDay(mDuration+pRH.mDuration);
    }

    ///returns the subtraction of \p pRH from this object
    CTimeOfDay operator-(const CTimeOfDay &pRH) const
    {
        return CTimeOfDay(mDuration-pRH.mDuration);
    }

    ///returns the result of multiplying this object by \p pRH
    CTimeOfDay operator*(int pRH) const
    {
        return CTimeOfDay(mDuration*pRH);
    }

    ///returns the result of dividing this object by \p pRH
    CTimeOfDay operator/(int pRH) const
    {
        return CTimeOfDay(mDuration/pRH);
    }

    ///returns the result of multiplying this object by \p pRH
    CTimeOfDay operator*(double pRH) const
    {
        return CTimeOfDay(chrono::nanoseconds((int64_t)((double)mDuration.count()*pRH)));
    }

    ///returns the result of dividing this object by \p pRH
    CTimeOfDay operator/(double pRH) const
    {
        return CTimeOfDay(chrono::nanoseconds((int64_t)((double)mDuration.count()/pRH)));
    }

    ///conversion from time duration
    template<typename tRep,typename tPeriod>
    explicit operator chrono::duration<tRep,tPeriod>()
    {
        return chrono::duration_cast<chrono::duration<tRep,tPeriod>>(mDuration);
    }

private:
    chrono::nanoseconds mDuration;
};

/*namespace buola*/ }

#endif
